#include "hook.h"
#include <dlfcn.h>
#include "demo04.h"

Logger::ptr g_logger = createLogger();

namespace sylar {

//是否需要重载函数的标志
static thread_local bool t_hook_enable = false;

#define HOOK_FUN(XX) \
    XX(sleep) \
    XX(usleep) \
    XX(nanosleep) \
    XX(socket) \
    XX(connect) \
    XX(accept) \
    XX(read) \
    XX(readv) \
    XX(recv) \
    XX(recvfrom) \
    XX(recvmsg) \
    XX(write) \
    XX(writev) \
    XX(send) \
    XX(sendto) \
    XX(sendmsg) \
    XX(close) \
    XX(fcntl) \
    XX(ioctl) \
    XX(getsockopt) \
    XX(setsockopt)

void hook_init() {
    static bool is_inited = false;
    if(is_inited) {
        return;
    }
    //name ## _f 拼接函数名，name ## _fun 函数类型指针
#define XX(name) name ## _f = (name ## _fun)dlsym(RTLD_NEXT, #name);
    HOOK_FUN(XX);
#undef XX
}

static uint64_t s_connect_timeout = -1;
//定义一个结构体
struct _HookIniter {
    _HookIniter() {
        // hook_init();
        // s_connect_timeout = g_tcp_connect_timeout->getValue();

        // g_tcp_connect_timeout->addListener([](const int& old_value, const int& new_value){
        //         std::cout << "tcp connect timeout changed from "
        //                                  << old_value << " to " << new_value << std::endl;
        //         s_connect_timeout = new_value;
        // });
    }
};

static _HookIniter s_hook_initer; //声明一个全局静态变量 会先在main函数之前执行

bool is_hook_enable() {
    return t_hook_enable;
}

void set_hook_enable(bool flag) {
    t_hook_enable = flag;
}

}

struct timer_info { //定义该结构体作为条件
    int cancelled = 0;
};

//统一调用被封装函数的模板函数
template<typename OriginFun, typename... Args>
//hook_fun_name:函数名
//OriginFun：hook的函数
//event：事件类型
//timeout_so：超时时间
static ssize_t do_io(int fd, OriginFun fun, const char* hook_fun_name,
        uint32_t event, int timeout_so, Args&&... args) {
    if(!sylar::t_hook_enable) { //不需要重载
        return fun(fd, std::forward<Args>(args)...); //返回原来的函数
    }

    FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd); //获取到对应的fd的FdCtx
    if(!ctx) { //不存在
        return fun(fd, std::forward<Args>(args)...); //需要按原来的方法执行
    }

    if(ctx->isClose()) { //已经关闭
        errno = EBADF; //表示文件有问题
        return -1;
    }

    if(!ctx->isSocket() || ctx->getUserNonblock()) { //不是socket或者已经设置过为非阻塞
        return fun(fd, std::forward<Args>(args)...); //按原来的方法执行
    }

    uint64_t to = ctx->getTimeout(timeout_so); //获取超时类型
    std::shared_ptr<timer_info> tinfo(new timer_info); //创建超时条件

retry:
    ssize_t n = fun(fd, std::forward<Args>(args)...); //执行函数
    while(n == -1 && errno == EINTR) { //没有数据 循环读取
        n = fun(fd, std::forward<Args>(args)...); //重新获取
    }
    if(n == -1 && errno == EAGAIN) { //没有函数
        //做IO操作
        IOManager* iom = IOManager::GetThis(); //创建一个IO调度器
        Timer::ptr timer; //定时器
        std::weak_ptr<timer_info> winfo(tinfo); //连接一个弱指针

        if(to != (uint64_t)-1) { //超时时间不为-1
            timer = iom->addConditionTimer(to, [winfo, fd, iom, event]() { //添加进定时器容器中
                auto t = winfo.lock(); //返回原指针
                if(!t || t->cancelled) { //没有错误直接返回
                    return;
                }
                t->cancelled = ETIMEDOUT; //设为错误
                iom->cancelEvent(fd, (IOManager::Event)(event)); //需要取消事件
            }, winfo);
        }

        int rt = iom->addEvent(fd, (IOManager::Event)(event)); //把事件添加到epoll事件中
        if(SYLAR_UNLIKELY(rt)) { //添加出错
            SYLAR_LOG_ERROR(g_logger) << hook_fun_name << " addEvent("
                << fd << ", " << event << ")";
            if(timer) {
                timer->cancel(); //取消该事件对应的定时器
            }
            return -1;
        } else {
            Fiber::YieldToHold(); //添加成功 需要把事件执行并且暂停
            if(timer) {
                timer->cancel(); //如果定时器不为空 取消定时器
            }
            if(tinfo->cancelled) { //有值 表示超时
                errno = tinfo->cancelled; 
                return -1; //出错返回-1
            }
            goto retry;
        }
    }
    
    return n; //读到数据直接返回n
}


extern "C" {
    //声明
#define XX(name) name ## _fun name ## _f = nullptr;
    HOOK_FUN(XX);
#undef XX

//重新定义同名函数
//到秒数
unsigned int sleep(unsigned int seconds) {
    if(!sylar::t_hook_enable) { //如果没有重载
        return sleep_f(seconds); //调用原来的函数
    }

    Fiber::ptr fiber = Fiber::GetThis(); //取出当前协程
    IOManager* iom = IOManager::GetThis(); //取出当前IO调度器
    iom->addTimer(seconds * 1000, std::bind((void(Scheduler::*)
            (Fiber::ptr, int thread))&IOManager::schedule
            ,iom, fiber, -1)); //添加到时间定时器中
    Fiber::YieldToHold(); //暂停
    return 0;
}

//到微妙数
int usleep(useconds_t usec) {
    if(!sylar::t_hook_enable) {
        return usleep_f(usec); // 调用原函数
    }
    Fiber::ptr fiber = Fiber::GetThis();
    IOManager* iom = IOManager::GetThis();
    iom->addTimer(usec / 1000, std::bind((void(Scheduler::*)
            (Fiber::ptr, int thread))&IOManager::schedule
            ,iom, fiber, -1));
    Fiber::YieldToHold();
    return 0;
}

//
int nanosleep(const struct timespec *req, struct timespec *rem) {
    if(!sylar::t_hook_enable) {
        return nanosleep_f(req, rem); 
    }

    int timeout_ms = req->tv_sec * 1000 + req->tv_nsec / 1000 /1000; //获取时间
    Fiber::ptr fiber = Fiber::GetThis();
    IOManager* iom = IOManager::GetThis();
    iom->addTimer(timeout_ms, std::bind((void(Scheduler::*)
            (Fiber::ptr, int thread))&IOManager::schedule
            ,iom, fiber, -1));
    Fiber::YieldToHold();
    return 0;
}

//socket重写
int socket(int domain, int type, int protocol) {
    if(!sylar::t_hook_enable) { //不需要重载
        return socket_f(domain, type, protocol); //使用原来的函数
    }
    int fd = socket_f(domain, type, protocol); //创建文件句柄
    if(fd == -1) {
        return fd;
    }
    FdMgr::GetInstance()->get(fd, true); //创建一个文件句柄类
    return fd;
}

int connect_with_timeout(int fd, const struct sockaddr* addr, socklen_t addrlen, uint64_t timeout_ms) {
    if(!sylar::t_hook_enable) { //不使用
        return connect_f(fd, addr, addrlen); 
    }
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd);
    if(!ctx || ctx->isClose()) { //不存在或者已经关闭
        errno = EBADF; //设置错误
        return -1;
    }

    if(!ctx->isSocket()) {
        return connect_f(fd, addr, addrlen);
    }

    if(ctx->getUserNonblock()) { //用户设置过了非阻塞
        return connect_f(fd, addr, addrlen); //直接返回
    }

    int n = connect_f(fd, addr, addrlen); //运行函数
    if(n == 0) {
        return 0;
    } else if(n != -1 || errno != EINPROGRESS) { //没有出错
        return n;
    }

    IOManager* iom = IOManager::GetThis(); //获取IO调度器
    Timer::ptr timer;
    std::shared_ptr<timer_info> tinfo(new timer_info); //创建超时条件
    std::weak_ptr<timer_info> winfo(tinfo);

    if(timeout_ms != (uint64_t)-1) {
        timer = iom->addConditionTimer(timeout_ms, [winfo, fd, iom]() { //添加到定时器容器
                auto t = winfo.lock();
                if(!t || t->cancelled) {
                    return;
                }
                t->cancelled = ETIMEDOUT; //设置超时条件
                iom->cancelEvent(fd, IOManager::WRITE); //取消事件
        }, winfo);
    }

    int rt = iom->addEvent(fd, IOManager::WRITE); //添加事件
    if(rt == 0) { //添加成功
        Fiber::YieldToHold(); //唤醒事件
        if(timer) {
            timer->cancel(); //取消定时器
        }
        if(tinfo->cancelled) {
            errno = tinfo->cancelled;
            return -1;
        }
    } else {
        if(timer) {
            timer->cancel(); //没有添加成功也取消定时器
        }
        SYLAR_LOG_ERROR(g_logger) << "connect addEvent(" << fd << ", WRITE) error";
    }

    int error = 0;
    socklen_t len = sizeof(int);
    if(-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len)) { //取出状态
        return -1;
    }
    if(!error) { //取失败
        return 0;
    } else {
        errno = error;
        return -1;
    }
}

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
    return connect_with_timeout(sockfd, addr, addrlen, sylar::s_connect_timeout);
}

//接收一个fd
int accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
    int fd = do_io(s, accept_f, "accept", IOManager::READ, SO_RCVTIMEO, addr, addrlen);
    if(fd >= 0) { //大于0表示一个产生了fd
        FdMgr::GetInstance()->get(fd, true); //需要初始化fd
    }
    return fd;
}

//读取信息重载
ssize_t read(int fd, void *buf, size_t count) {
    return do_io(fd, read_f, "read", IOManager::READ, SO_RCVTIMEO, buf, count);
}

ssize_t readv(int fd, const struct iovec *iov, int iovcnt) {
    return do_io(fd, readv_f, "readv", IOManager::READ, SO_RCVTIMEO, iov, iovcnt);
}

ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
    return do_io(sockfd, recv_f, "recv", IOManager::READ, SO_RCVTIMEO, buf, len, flags);
}

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) {
    return do_io(sockfd, recvfrom_f, "recvfrom", IOManager::READ, SO_RCVTIMEO, buf, len, flags, src_addr, addrlen);
}

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) {
    return do_io(sockfd, recvmsg_f, "recvmsg", IOManager::READ, SO_RCVTIMEO, msg, flags);
}

//写信息重载
ssize_t write(int fd, const void *buf, size_t count) {
    return do_io(fd, write_f, "write", IOManager::WRITE, SO_SNDTIMEO, buf, count);
}

ssize_t writev(int fd, const struct iovec *iov, int iovcnt) {
    return do_io(fd, writev_f, "writev", IOManager::WRITE, SO_SNDTIMEO, iov, iovcnt);
}

//发送信息重载
ssize_t send(int s, const void *msg, size_t len, int flags) {
    return do_io(s, send_f, "send", IOManager::WRITE, SO_SNDTIMEO, msg, len, flags);
}

ssize_t sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) {
    return do_io(s, sendto_f, "sendto", IOManager::WRITE, SO_SNDTIMEO, msg, len, flags, to, tolen);
}

ssize_t sendmsg(int s, const struct msghdr *msg, int flags) {
    return do_io(s, sendmsg_f, "sendmsg", IOManager::WRITE, SO_SNDTIMEO, msg, flags);
}

int close(int fd) {
    if(!sylar::t_hook_enable) { //不使用重载
        return close_f(fd); //
    }

    FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd); //获取到fd的对应类
    if(ctx) {
        auto iom = IOManager::GetThis(); //获取调度器
        if(iom) {
            iom->cancelAll(fd); //把该fd对应的所有事件取消
        }
        FdMgr::GetInstance()->del(fd); //关闭fd
    }
    return close_f(fd);
}

//设置非阻塞    
int fcntl(int fd, int cmd, ... /* arg */ ) {
    va_list va;
    va_start(va, cmd);
    switch(cmd) {
        //设置文件句柄为非阻塞
        case F_SETFL:
            {
                int arg = va_arg(va, int); //不定参数列表
                va_end(va);
                FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd); //获取到句柄
                if(!ctx || ctx->isClose() || !ctx->isSocket()) { //不存在
                    return fcntl_f(fd, cmd, arg); //直接使用原来的返回
                }
                ctx->setUserNonblock(arg & O_NONBLOCK); //是否是用户设置非阻塞
                if(ctx->getSysNonblock()) { //系统添加的非阻塞
                    arg |= O_NONBLOCK;
                } else {
                    arg &= ~O_NONBLOCK; //去除非阻塞标志
                }
                return fcntl_f(fd, cmd, arg); //返回
            }
            break;
        //文件句柄
        case F_GETFL: //int
            {
                va_end(va); //获取到参数列
                int arg = fcntl_f(fd, cmd); //取出参数
                FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd);
                if(!ctx || ctx->isClose() || !ctx->isSocket()) { //不存在或者已关闭
                    return arg; //直接返回
                }
                if(ctx->getUserNonblock()) {
                    return arg | O_NONBLOCK; //添加系统非阻塞标志
                } else {
                    return arg & ~O_NONBLOCK; //去除
                }
            }
            break;
        //int 类型
        case F_DUPFD: //
        case F_DUPFD_CLOEXEC:  //文件相关的(输入参数是int)
        case F_SETFD: //int
        case F_SETOWN: //int
        case F_SETSIG: //int
        case F_SETLEASE: //int
        case F_NOTIFY: //int
#ifdef F_SETPIPE_SZ
        case F_SETPIPE_SZ: //int
#endif
            {
                int arg = va_arg(va, int);
                va_end(va);
                return fcntl_f(fd, cmd, arg); 
            }
            break;
        case F_GETFD: //void
        case F_GETOWN: //void
        case F_GETSIG: //void
        case F_GETLEASE: //void
#ifdef F_GETPIPE_SZ
        case F_GETPIPE_SZ: //void
#endif
            {
                va_end(va);
                return fcntl_f(fd, cmd);
            }
            break;
        //struct flok
        case F_SETLK: 
        case F_SETLKW:
        case F_GETLK:
            {
                struct flock* arg = va_arg(va, struct flock*); //创建一个对应的结构体
                va_end(va);
                return fcntl_f(fd, cmd, arg);
            }
            break;
        //struct f_owner_ex
        case F_GETOWN_EX:
        case F_SETOWN_EX:
            {
                struct f_owner_exlock* arg = va_arg(va, struct f_owner_exlock*); //创建一个对应的结构体
                va_end(va);
                return fcntl_f(fd, cmd, arg);
            }
            break;
        default:
            va_end(va);
            return fcntl_f(fd, cmd);
    }
}

int ioctl(int d, unsigned long int request, ...) {
    //获取不定参数列表中的参数
    va_list va;
    va_start(va, request);
    void* arg = va_arg(va, void*); 
    va_end(va);

    if(FIONBIO == request) {
        bool user_nonblock = !!*(int*)arg; //两个！！是把内容转换成bool值
        FdCtx::ptr ctx = FdMgr::GetInstance()->get(d);
        if(!ctx || ctx->isClose() || !ctx->isSocket()) { //不存在或者已经关闭
            return ioctl_f(d, request, arg);
        }
        ctx->setUserNonblock(user_nonblock); //手动设置为非阻塞
    }
    return ioctl_f(d, request, arg); //否则直接返回
}

//getsockopt 函数用于检索与套接字相关的信息，如当前的套接字状态、配置或行为
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
    return getsockopt_f(sockfd, level, optname, optval, optlen); //直接返回原函数
}

//setsockopt 函数用于设置套接字选项，这些选项可以配置套接字的行为
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
    if(!sylar::t_hook_enable) { //不使用重载函数
        return setsockopt_f(sockfd, level, optname, optval, optlen); //直接返回原函数
    }
    if(level == SOL_SOCKET) {
        if(optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
            FdCtx::ptr ctx = FdMgr::GetInstance()->get(sockfd);
            if(ctx) {
                const timeval* v = (const timeval*)optval; //转换为timeval类型
                ctx->setTimeout(optname, v->tv_sec * 1000 + v->tv_usec / 1000); //设置超时之间
            }
        }
    }
    return setsockopt_f(sockfd, level, optname, optval, optlen); //返回原函数
}

}
